﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.ComponentModel.Composition;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json.Converters;
using Lhn.Ds.Core.Exceptions;

namespace Lhn.Ds.Core.Service
{
    [Export(typeof(Client))]
    public class Client
    {
        private string _apiUrl = "https://www.yikuaichong.cn/api/v1";

        //private string _apiUrl = "https://192.168.1.154:8002/api";
        private readonly HttpClient _client;

        private readonly JsonSerializerSettings _firstLowerJsonSerializerSettings =
            new JsonSerializerSettings {ContractResolver = new FristLowerContractResolver()};

        private readonly JsonSerializerSettings _firstUpperJsonSerializerSettings =
            new JsonSerializerSettings {ContractResolver = new FristUpperContractResolver()};

        public Client()
        {
            _firstUpperJsonSerializerSettings.Converters.Add(new JsonDateTimeConvert());
            _firstLowerJsonSerializerSettings.Converters.Add(new JsonDateTimeConvert());
            _client = new HttpClient();
            _client.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
            _client.DefaultRequestHeaders.Add("Keep-Alive", "timeout=600");
            //DsContext.InstanceData.IOC.ComposeParts(this);
        }

        private string GetUrl(string api)
        {
            if (api.StartsWith("/"))
            {
                return _apiUrl + api;
            }
            else
            {
                return _apiUrl + "/" + api;
            }
        }

        private T DoRequest<T>(Func<T> action)
        {
            try
            {
                return action.Invoke();
            }
            catch (JsonException)
            {
                throw new RemoteDataException();
            }
            catch (System.Exception e)
            {
                throw new SysException(e, "请求发生错误");
            }
        }

        private async Task<T> ProcessResponse<T>(HttpResponseMessage rst, string conent)
        {
            if (rst.StatusCode != HttpStatusCode.OK)
            {
                throw new ResposeFailException(new ResponseInfo()
                {
                    ResponseBody = await rst.Content.ReadAsStringAsync(),
                    ResponseHeaders = rst.Headers,
                    RequestBody = conent,
                    RequestHeader = rst.RequestMessage.Headers
                });
            }

            var rstBody = await rst.Content.ReadAsStringAsync();
            if (string.IsNullOrWhiteSpace(rstBody) ||
                string.Equals(rstBody, "null", StringComparison.CurrentCultureIgnoreCase))
            {
                return default(T);
            }

            if (typeof(T) == typeof(string))
            {
                return (T) Convert.ChangeType(rstBody, typeof(T));
            }

            if (typeof(T) == typeof(object))
            {
                return default(T);
            }

            try
            {
                return JsonConvert.DeserializeObject<T>(rstBody, _firstUpperJsonSerializerSettings);
            }
            catch (System.Exception e)
            {
                throw new ResposeProccessException(new ResponseInfo()
                {
                    ResponseBody = await rst.Content.ReadAsStringAsync(),
                    ResponseHeaders = rst.Headers,
                    RequestBody = conent,
                    RequestHeader = rst.RequestMessage.Headers
                }, e);
            }
        }

        public async Task<T> Post<T, TK>(string api, TK body, bool isJsonTYpe = false)
        {
            if (typeof(TK) == typeof(string))
            {
                return await Post<T>(api, body as string);
            }

            if (typeof(TK) == typeof(FormUrlEncodedContent))
            {
                return await Post<T>(api, body as FormUrlEncodedContent);
            }

            if (isJsonTYpe)
            {
                return await Post<T>(api,
                    JsonConvert.SerializeObject(body, Formatting.Indented, _firstLowerJsonSerializerSettings));
            }

            return await Post<T>(api, ModelToUriParam(body));
        }

        public async Task<T> Post<T>(string api, string body, bool isJsonTye = false)
        {
            return await DoRequest(async () =>
            {
                HttpResponseMessage rst;
                try
                {
                    rst = await _client.PostAsync(GetUrl(api),
                        new StringContent(body)
                        {
                            Headers =
                            {
                                ContentType =
                                    isJsonTye
                                        ? MediaTypeHeaderValue.Parse("application/json;charset=UTF-8")
                                        : MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded;charset=UTF-8")
                            }
                        });
                }
                catch (Exception e)
                {
                    throw new SytechHttpException(e);
                }

                return await ProcessResponse<T>(rst, body);
            });
        }

        public async Task<T> Post<T>(string api, FormUrlEncodedContent content)
        {
            return await DoRequest(async () =>
            {
                _processContent(content);
                HttpResponseMessage rst;
                try
                {
                    rst = await _client.PostAsync(GetUrl(api), content);
                }
                catch (Exception e)
                {
                    throw new SytechHttpException(e);
                }
                return await ProcessResponse<T>(rst, "无法获取请求的内容");
            });
        }

        private DateTime DefaultMinTime = DateTime.Parse("1970-1-1");

        public string ModelToUriParam(object obj)
        {
            PropertyInfo[] propertis = obj.GetType().GetProperties();
            StringBuilder sb = new StringBuilder();
            foreach (var p in propertis)
            {
                var v = p.GetValue(obj, null);
                if (v == null)
                    continue;
                sb.Append(new string(p.Name.ToCharArray().Select((c, i) => i == 0 ? char.ToLower(c) : c).ToArray()));
                sb.Append("=");
                if (p.PropertyType == typeof(DateTime))
                {
                    var d = v is DateTime ? (DateTime) v : DefaultMinTime;
                    var dif = (long) d.Subtract(DefaultMinTime).TotalMilliseconds;
                    if (dif < 0) dif = 0;
                    sb.Append(HttpUtility.UrlEncode(dif.ToString()));
                }
                else
                {
                    sb.Append(HttpUtility.UrlEncode(v.ToString()));
                }

                sb.Append("&");
            }

            sb.Remove(sb.Length - 1, 1);
            return sb.ToString();
        }

        private void _processContent(FormUrlEncodedContent content)
        {
            content.Headers.ContentType.CharSet = "utf-8";
        }
    }

    public class SytechHttpException : SytechException
    {
        public SytechHttpException(Exception innerException, string message = "请求服务器错误") : base(innerException, message)
        {
        }
    }

    public class RemoteDataException : SysException
    {
        public RemoteDataException(string message = "远程数据格式错误") : base(message)
        {
        }
    }

    public class FristLowerContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return new string(propertyName.ToCharArray().Select((c, i) => i == 0 ? char.ToLower(c) : c).ToArray());
        }
    }

    public class FristUpperContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return new string(propertyName.ToCharArray().Select((c, i) => i == 0 ? char.ToUpper(c) : c).ToArray());
        }
    }

    public class JsonDateTimeConvert : CustomCreationConverter<DateTime>
    {
        private DateTime StartDateTime = new DateTime(1970, 1, 1);

        public override DateTime Create(Type objectType)
        {
            return new DateTime(1970, 1, 1);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null)
            {
                writer.WriteNull();
            }
            else
            {
                var d = (DateTime) value;
                var dif = (long) d.Subtract(StartDateTime).Milliseconds;
                if (dif < 0)
                {
                    dif = 0;
                }

                writer.WriteValue(dif);
            }
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
            JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
            {
                return null;
            }

            var val = reader.Value.ToString();
            var l = 0l;
            if (long.TryParse(val, out l))
            {
                return StartDateTime.AddMilliseconds(l);
            }
            else
            {
                return DateTime.Parse(val);
            }
        }
    }
}